
function PetriNetExecutor(petri, option) {
	this.petri = petri;
	option = option || {};
	this.option = {};
	this.running = null;
	this.stoped = true;
	this.history = {
		index: 0,
		data: [petri],
		length: 1
	};
	for (var i in PetriNetExecutor.option) {
		this.option[i] = (i in option) ? option[i] : PetriNetExecutor.option[i];
	}
}

PetriNetExecutor.option = {
	delay: 300, //ms
	conflic: function(nodes) {
		var i = Math.floor(Math.random() * nodes.length);
		return nodes[i];
	},
	onAutoRun: null
}

void function() {

var fn = PetriNetExecutor.prototype;

fn.fire = function() {
	var self = this,
		cur_petri = self.getCurPetriNet(),
		petri = cur_petri.clone();

	// fire petri
	var transitions = petri.getEnableTransitions(); // array [transitions...]
	while (transitions.length) {
		var cur = transitions[0],
			conflicTransitions = transitions.filter(function(tr) {
				return petri.isConflic(cur, tr); // true | false
			});
		cur = self.option.conflic(conflicTransitions);
		cur.eatToken();
		conflicTransitions.filter(function(tr) {
			if (cur == tr) {
				return true;
			}
			return !tr.isEnable();
		}).forEach(function(tr) {
			transitions.splice(transitions.indexOf(tr), 1);
		})
		cur.addToken();
	}

	if (petri.state() != cur_petri.state() /* json string: [[place_name, tokens]....] */) {
		// record newing petri
		self.history.index++;
		self.history.length = self.history.index + 1;
		self.history.data[self.history.index] = petri;
	} else {
		// petri net not changed
	}

		
	return self;
}

fn.getCurPetriNet = function() {
	return this.history.data[this.history.index];
}

fn.autoRun = function(time) {
	if (!arguments.length) {
		time = time || -1;
	}
	var self = this, cur = 0,
	next = function() {
		self.stoped = false;
		self.running = setTimeout(function() {
			cur += self.option.delay;
			if (time != -1 && cur > time) {
				return self.stop();
			}
			self.fire();
			self.option.onAutoRun && self.option.onAutoRun.fireWith(self, [self.getCurPetriNet()]);
			return next();
		}, self.option.delay);
	};
	next();
	return self;
}

fn.stop = function() {
	var self = this;
	self.stoped = true;
	self.running && clearTimeout(self.running);
	self.running = null;
	return self;
}

fn.backward = function(step) {
	if (!arguments.length) {
		step = 1;
	}
	step = step || 0;
	var self = this, history = self.history;
	history.index -= step;
	history.index = Math.max(0, history.index);
	history.index = Math.min(history.length - 1, history.index);
	return self;
}

fn.forward = function(step) {
	if (!arguments.length) {
		step = 1;
	}
	step = step || 0;
	var self = this, history = self.history;
	history.index += step;
	history.index = Math.max(0, history.index);
	history.index = Math.min(history.length - 1, history.index);
	return self;
}

fn.getLength = function() {
	return this.history.length;
}

}()